import configparser
import sys
import traceback
import threading
import os

sys.path.append("/netboxSyncZabbix")
from zabbix_sync_script.netbox_update_to_zabbix import zabbix_sync_lines_6, zabbix_sync_hosts_62, \
    zabbix_sync_group_62, zabbix_sync_lines_62, zabbix_sync_virtual
from api import zabbix_api62, netbox_api, zabbix_api6, weixin_webhook
from loguru import logger

logger.add("/var/log/Task/netbox/Tasks.log", rotation="00:00", backtrace=True, diagnose=True, level="DEBUG")

# 获取配置文件
dir_name = os.path.dirname(os.path.dirname(__file__))
ini_path = f"{dir_name}/config.ini"

config = configparser.RawConfigParser()
config.read(ini_path)

# 关联脚本变量

# 公共环境变量
limit = config.get("netbox", "limit")
verify = config.getboolean("netbox", "verify")
father_template_group = config.get("global", "father_template_group")
proxy01_id = config.get("zabbix", "proxy01_id")

# 正式环境变量
# netbox
netbox_url = config.get("netbox", "url")
netbox_token = config.get("netbox", "token")
# zabbix
zabbix_server_url = config.get("zabbix", "url")
zabbix_user = config.get("zabbix", "user")
zabbix_passwd = config.get("zabbix", "passwd")
# zabbix SRE
sre_zabbix_server_url = config.get("zabbix", "sre_url")
sre_zabbix_user = config.get("zabbix", "sre_user")
sre_zabbix_passwd = config.get("zabbix", "sre_password")
# webhook
webhook_url = config.get("global", "webhook_url")
webhook_Circuits_Notify = config.get("global", "webhook_Circuits-Notify")


# # 测试环境变量
# # netbox
# netbox_url = config.get("netbox", "test_url")
# netbox_token = config.get("netbox", "test_token")
# # zabbix
# zabbix_server_url = config.get("zabbix_test", "url")
# zabbix_user = config.get("zabbix_test", "user")
# zabbix_passwd = config.get("zabbix_test", "passwd")
# # zabbix SRE
# sre_zabbix_server_url = config.get("zabbix_test", "url")
# sre_zabbix_user = config.get("zabbix_test", "user")
# sre_zabbix_passwd = config.get("zabbix_test", "passwd")
# # webhook
# webhook_url = config.get("global", "webhook_test")
# webhook_Circuits_Notify = config.get("global", "webhook_test")


# Create your views here.
class MyThread(threading.Thread):
    """
        某个类没有我要的方法，那么可以重写这个类，添加我需要的方法
    """

    def __init__(self, target=None, args=()):
        super(MyThread, self).__init__()
        self.func = target
        self.args = args
        self.result = None

    def run(self):
        try:
            self.result = self.func(*self.args)
        except Exception:
            logger.exception(traceback.format_exc())
            logger.info(traceback.format_exc())

    def get_result(self):
        return self.result


def netboxupdate(dict_data, dict_model):
    """
    该功能用于用户系统（netbox）调用webhook实现自动更新 host、group、line 的数据
    :param dict_data:
    :param dict_model:
    :return: 更新 {line / host/ group} / 更新失败
    """

    # -----获取zabbix token-----------
    try:
        token = zabbix_api62.zabbix_get_token(zabbix_user, zabbix_passwd, zabbix_server_url, verify)
    except Exception as e:
        resp_data = {"status": "Error", "message": f"获取zabbix token错误", "detail": traceback.format_exc()}
        logger.info(resp_data)
        logger.exception(traceback.format_exc())
        return resp_data

    line_model_list = ["circuit", "cable", "circuittermination"]
    try:
        logger.info(f"------------------------------------------更新{dict_model}-------------------------------------")
        logger.info("========================================================")
        logger.info(dict_data)
        logger.info("========================================================")
        # 站点信息更新
        if dict_model == "site":
            if dict_data.get("tenant") and dict_data["tenant"]["name"] == "SRE":
                resp_data = zabbix_sync_group_62.zabbix_update_group(dict_data, netbox_url, netbox_token, limit,
                                                                     verify,
                                                                     sre_zabbix_server_url, sre_zabbix_user,
                                                                     sre_zabbix_passwd, webhook_Circuits_Notify)
            else:
                resp_data = zabbix_sync_group_62.zabbix_update_group(dict_data, netbox_url, netbox_token, limit,
                                                                     verify,
                                                                     zabbix_server_url, zabbix_user, zabbix_passwd,
                                                                     webhook_Circuits_Notify)
        # 设备信息更新
        elif dict_model == "device":
            if dict_data.get("device_role") and dict_data.get("device_role").get("name") == "Virtualized Server":
                resp_data = {"status": "None",
                             "message": "该设备device_role为Virtualized Server，本次不更新，采用虚拟机逻辑进行更新",
                             "detail": f"该设备device_role为Virtualized Server，本次不更新，采用虚拟机逻辑进行更新",
                             "hostname": dict_data.get("name")}
                return resp_data
            if dict_data.get("tenant") and dict_data.get("tenant").get("name") == "SRE":
                sre_token = zabbix_api62.zabbix_get_token(sre_zabbix_user, sre_zabbix_passwd, sre_zabbix_server_url,
                                                          verify)
                if type(sre_token) == dict and sre_token.get("status") == "Error":
                    sre_token["hostname"] = dict_data["name"]
                    return sre_token
                resp_data = zabbix_sync_hosts_62.creat_hosts(dict_data, netbox_token, verify, sre_token,
                                                             sre_zabbix_server_url,
                                                             father_template_group, netbox_url)
            else:
                if dict_data.get("tenant") and dict_data["tenant"]["name"] == "Netops":
                    proxy_hostid = proxy01_id
                else:
                    proxy_hostid = proxy01_id
                resp_data = zabbix_sync_hosts_62.creat_hosts(dict_data, netbox_token, verify, token,
                                                             zabbix_server_url,
                                                             father_template_group, netbox_url)
        # 线路连接更新
        elif dict_model == "cable":
            # 更新zabbix线路
            if dict_data.get("tenant") and dict_data.get("tenant").get("name") == "SRE":
                sre_token = zabbix_api62.zabbix_get_token(sre_zabbix_user, sre_zabbix_passwd, sre_zabbix_server_url,
                                                          verify)
                if type(sre_token) == dict and sre_token.get("status") == "Error":
                    sre_token["hostname"] = dict_data["name"]
                    return sre_token
                resp_data = zabbix_sync_lines_62.update_cables(dict_data, sre_token, sre_zabbix_server_url,
                                                               netbox_token,
                                                               father_template_group, netbox_url,
                                                               proxy_hostid=proxy01_id)

            else:
                resp_data = zabbix_sync_lines_62.update_cables(dict_data, token, zabbix_server_url, netbox_token,
                                                               father_template_group, netbox_url,
                                                               proxy_hostid=proxy01_id)

        # 外线信息更新
        elif dict_model == "circuit":
            # 更新zabbix 线路
            cable_info = zabbix_sync_lines_6.get_cable_info(dict_data, netbox_url, netbox_token, verify=verify)
            if cable_info.get("status") == "Error":
                return cable_info

            if dict_data.get("tenant") and dict_data.get("tenant").get("name") == "SRE":
                sre_token = zabbix_api62.zabbix_get_token(sre_zabbix_user, sre_zabbix_passwd, sre_zabbix_server_url,
                                                          verify)
                if type(sre_token) == dict and sre_token.get("status") == "Error":
                    sre_token["hostname"] = cable_info["name"]
                    return sre_token
                resp_data = zabbix_sync_lines_62.update_cables(cable_info, sre_token, sre_zabbix_server_url,
                                                               netbox_token, father_template_group,
                                                               netbox_url, proxy_hostid=proxy01_id)
            else:
                resp_data = zabbix_sync_lines_62.update_cables(cable_info, token, zabbix_server_url, netbox_token,
                                                               father_template_group,
                                                               netbox_url, proxy_hostid=proxy01_id)

        elif dict_model == "virtualmachine":
            resp_data = zabbix_sync_virtual.zabbix_update_virtual(dict_data, netbox_token, verify,
                                                                  token, zabbix_server_url,
                                                                  father_template_group, netbox_url)
            logger.info(f"虚拟机更新结果:{resp_data}")
        else:
            resp_data = {"status": "error", "message": "未匹配更新类型"}
        logger.info(f"resp_data----->{resp_data}")
    except Exception as e:
        logger.exception(traceback.format_exc())
        logger.info(traceback.format_exc())
        resp_data = {"status": "Error", "message": "更新失败", "detail": traceback.format_exc(), "data": dict_data}
        logger.error(resp_data)

    logger.info("请求结束")
    logger.info(resp_data)
    return resp_data


def classified_execution(dict_model, netbox_url):
    error_list = []
    update_list = []
    create_list = []
    discard_list = []
    if dict_model == "site":
        # '''
        # -----获取zabbix token-----------
        token = zabbix_api6.zabbix_get_token(zabbix_user, zabbix_passwd, zabbix_server_url, verify)

        # 更新Group
        # --------从netbox获取group信息-------------
        groupList = netbox_api.netbox_get_group(netbox_url, netbox_token, limit, verify)
        firstDataList = groupList[0:int(limit)]
        # dict_model = 'site'
        for i in firstDataList:
            resp = netboxupdate(i, dict_model)
            if resp:
                if resp["status"] == "Update":
                    update_list.append(resp)
                elif resp["status"] == "Create":
                    create_list.append(resp)
                elif resp["status"] == "Error":
                    error_list.append(resp)
                elif resp["status"] == "Discard":
                    discard_list.append(resp)

    elif dict_model == "device":
        # 更新Hosts
        error_list = []
        update_list = []
        create_list = []
        discard_list = []
        dataList = netbox_api.netbox_get_hosts(netbox_url, netbox_token, '100', '0', verify)
        # 从字典里取 data 数组
        hostsList = dataList.get('results')
        nexturl = dataList.get('next')
        # 总数
        hostscount = dataList.get('count')
        for i in hostsList:
            resp = netboxupdate(i, dict_model)
            if resp["status"] == "Update":
                update_list.append(resp)
            elif resp["status"] == "Create":
                create_list.append(resp)
            elif resp["status"] == "Error":
                error_list.append(resp)
            elif resp["status"] == "Discard":
                discard_list.append(resp)

        while nexturl:
            dataList = netbox_api.netbox_get_next(netbox_token, nexturl, verify)
            hostsList = dataList.get('results')
            nexturl = dataList.get('next')
            for j in hostsList:
                resp = netboxupdate(j, dict_model)
                if resp["status"] == "Update":
                    update_list.append(resp)
                elif resp["status"] == "Create":
                    create_list.append(resp)
                elif resp["status"] == "Error":
                    error_list.append(resp)
                elif resp["status"] == "Discard":
                    discard_list.append(resp)

    # elif dict_model == "circuit":
    elif dict_model == "cable":
        # 更新线路
        netbox_list = 0
        dataList = netbox_api.netbox_get_cables(netbox_url, netbox_token, False)
        # 从字典里取 data 数组
        cablesList = dataList.get('results')
        nexturl = dataList.get('next')
        count = dataList.get('count')
        for i in cablesList:
            resp = netboxupdate(i, dict_model)
            if resp["status"] == "Update":
                update_list.append(resp)
            elif resp["status"] == "Create":
                create_list.append(resp)
            elif resp["status"] == "Error":
                error_list.append(resp)
            elif resp["status"] == "Discard":
                discard_list.append(resp)
        while nexturl:
            netbox_list = netbox_list + 1
            logger.info("拉取Netbox_host NextList" + str(netbox_list + 1) + "···")
            dataList = netbox_api.netbox_get_next(netbox_token, nexturl, False)
            cablesList = dataList.get('results')
            nexturl = dataList.get('next')
            for j in cablesList:
                resp = netboxupdate(j, dict_model)
                if resp["status"] == "Update":
                    update_list.append(resp)
                elif resp["status"] == "Create":
                    create_list.append(resp)
                elif resp["status"] == "Error":
                    error_list.append(resp)
                elif resp["status"] == "Discard":
                    discard_list.append(resp)
    elif dict_model == "virtualmachine":
        virtual_url = f"{netbox_url}/api/virtualization/virtual-machines/"
        virtual_dataList = netbox_api.netbox_get_next(netbox_token, virtual_url, False)

        # 从字典里取 data 数组
        hostsList = virtual_dataList.get('results')
        nexturl = virtual_dataList.get('next')
        # 总数
        hostscount = virtual_dataList.get('count')
        if hostsList:
            for i in hostsList:
                resp = netboxupdate(i, dict_model)
                if resp["status"] == "Update":
                    update_list.append(resp)
                elif resp["status"] == "Create":
                    create_list.append(resp)
                elif resp["status"] == "Error":
                    error_list.append(resp)
                elif resp["status"] == "Discard":
                    discard_list.append(resp)

        while nexturl:
            dataList = netbox_api.netbox_get_next(netbox_token, nexturl, verify)
            hostsList = dataList.get('results')
            nexturl = dataList.get('next')
            for j in hostsList:
                resp = netboxupdate(j, dict_model)
                if resp["status"] == "Update":
                    update_list.append(resp)
                elif resp["status"] == "Create":
                    create_list.append(resp)
                elif resp["status"] == "Error":
                    error_list.append(resp)
                elif resp["status"] == "Discard":
                    discard_list.append(resp)

    resp_dict = {
        "error_list": error_list,
        "update_list": update_list,
        "create_list": create_list,
        "discard_list": discard_list
    }
    logger.info(resp_dict)
    return resp_dict


def check_template(zabbix_user, zabbix_passwd, zabbix_server_url, verify):
    try:
        logger.info(f"开始检查Netbox Template 模板组内的模板")
        # 只连接了主机却未连接父模板的模板
        not_parent_templates_list = []

        # 1. 获取zabbix的Token
        token = zabbix_api62.zabbix_get_token(zabbix_user, zabbix_passwd, zabbix_server_url, verify)

        # 2. 获取Netbox Template模板组信息
        netbox_templates_info = zabbix_api62.zabbix_get_templategroup(token, zabbix_server_url, 'Netbox Templates')
        netbox_templates_id = netbox_templates_info[0]['groupid']

        # 3. 获取Netbox Template模板组下所有的模板
        templates_params = {'output': 'extend', "groupids": netbox_templates_id, }
        templates_list = zabbix_api62.template_custom_get(token, zabbix_server_url, templates_params)

        # 3. 遍历检查每个模板是否连接了主机却未连接模板

        for template in templates_list:
            params = {
                'output': 'extend',
                "templateids": template['templateid'],
                'selectValueMaps': ['valuemapid', 'name', 'mappings'],
                'selectTags': ['tag', 'value'],
                'selectMacros': 'extend',
                'selectParentTemplates': 'extend',
                'selectHosts': 'extend'
            }
            template_info = zabbix_api62.template_custom_get(token, zabbix_server_url, params)
            if template_info[0].get('hosts') and not template_info[0].get('parentTemplates'):
                not_parent_templates_list.append(template_info[0]['name'])
        logger.info(f"检查Red Template成功, 应检查模板为：{not_parent_templates_list}")
        return not_parent_templates_list
    except Exception:
        logger.error(traceback.format_exc())
        resp_data = {"status": "Error", "message": "检查Red Template模板组内模板链接失败", "detail": traceback.format_exc()}
        logger.error(resp_data)
        return resp_data


if __name__ == '__main__':

    # 同步站点
    site_dict = classified_execution("site", netbox_url)
    site_error_list = site_dict["error_list"]
    site_update_list = site_dict["update_list"]
    site_create_list = site_dict["create_list"]
    site_discard_list = site_dict["discard_list"]

    # 同步设备
    t1 = MyThread(classified_execution, args=("device", netbox_url))
    # # 同步线路
    # t2 = MyThread(classified_execution, args=("cable", netbox_url))
    t1.start()
    # t2.start()
    t1.join()
    # t2.join()
    device_dict = t1.result
    # cable_dict = t2.result

    device_error_list = device_dict["error_list"]
    device_update_list = device_dict["update_list"]
    device_create_list = device_dict["create_list"]
    device_discard_list = device_dict["discard_list"]

    # cable_error_list = cable_dict["error_list"]
    # cable_update_list = cable_dict["update_list"]
    # cable_create_list = cable_dict["create_list"]
    # cable_discard_list = cable_dict["discard_list"]

    # # 同步虚拟机
    # virtual_dict = classified_execution("virtualmachine", netbox_url)
    # virtual_error_list = virtual_dict["error_list"]
    # virtual_update_list = virtual_dict["update_list"]
    # virtual_create_list = virtual_dict["create_list"]
    # virtual_discard_list = virtual_dict["discard_list"]

    # 检查Red Template 模板
    not_parent_templates_list = check_template(zabbix_user, zabbix_passwd, zabbix_server_url, verify)

    site_error_str = ""
    device_error_str = ""
    # cable_error_str = ""
    # virtual_error_str = ""
    if site_error_list:
        site_error_str = f" {[i['hostname'] + ':' + i['detail'] for i in site_error_list]} \n"
    if device_error_list:
        device_error_str = f" {[i['hostname'] + ':' + i['detail'] for i in device_error_list]} \n"
    # if cable_error_list:
    #     cable_error_str = f" {[i['hostname'] + ':' + i['detail'] for i in cable_error_list]} \n"
    # if virtual_error_list:
    #     virtual_error_str = f" {[i['hostname'] + ':' + i['detail'] for i in virtual_error_list]} \n"
    logger.error(f"站点更新失败信息：{site_error_str}")
    logger.error(f"设备更新失败信息：{device_error_str}")
    # logger.error(f"线路更新失败信息：{cable_error_str}")
    # logger.error(f"虚拟机更新失败信息：{virtual_error_str}")

    content = f"## Zabbix同步报告: \n" \
              f">**Sites 同步报告:**\n" \
              f"更新{len(site_update_list)}台\n" \
              f"创建{len(site_create_list)}台\n" \
              f"弃用{len(site_discard_list)}台\n" \
              f"失败{len(site_error_list)}台:\n" \
              f">**Hosts 同步报告:**\n" \
              f"更新{len(device_update_list)}台\n" \
              f"创建{len(device_create_list)}台\n" \
              f"弃用{len(device_discard_list)}台\n" \
              f"失败{len(device_error_list)}台:\n"
              # f">**Lines 同步报告:**\n" \
              # f"更新{len(cable_update_list)}台\n" \
              # f"创建{len(cable_create_list)}台\n" \
              # f"弃用{len(cable_discard_list)}台\n" \
              # f"失败{len(cable_error_list)}台:\n" \
              # f">**Virtual Machines 同步报告:**\n" \
              # f"更新{len(virtual_update_list)}台\n" \
              # f"创建{len(virtual_create_list)}台\n" \
              # f"弃用{len(virtual_discard_list)}台\n" \
              # f"失败{len(virtual_error_list)}台:\n"
    if not_parent_templates_list:
        if type(not_parent_templates_list) == list:
            content += f">**以下组未链接模板，请检查:** <@haoyuejiao@xiaohongshu.net>\n" \
                       f"{not_parent_templates_list}\n"
        elif type(not_parent_templates_list) == dict:
            content += f">**检查模板链接失败:** <@machong@xiaohongshu.net>\n" \
                       f"{not_parent_templates_list.get('message')}\n"
    logger.info("同步报告：\n", content)
    response = weixin_webhook.send_to_webhook(webhook_url, content)
    logger.info(response)

